#include "imagewidget.h"
#include "global.h"
#include <math.h>

void ImageWidget::setAreaTool(bool flag) { m_areaTool = flag; }

ImageWidget::ImageWidget(QWidget *parent) :
  QWidget(parent)
{
  setFocusPolicy(Qt::StrongFocus);
  
  setMouseTracking(true);

  m_Depth = m_Width = m_Height = 0;
  m_imgHeight = 100;
  m_imgWidth = 100;
  m_simgHeight = 100;
  m_simgWidth = 100;
  m_simgX = 10;
  m_simgY = 20;


  m_zoom = 1;
  m_areaTool = true;
  Global::setSpread(20);

  m_tagColors.clear();

  m_lut = new uchar[4*256*256];
  memset(m_lut, 0, 4*256*256);

  m_image = QImage(100, 100, QImage::Format_RGB32);
  m_imageScaled = QImage(100, 100, QImage::Format_RGB32);
  m_maskimage = QImage(100, 100, QImage::Format_Indexed8);
  m_maskimageScaled = QImage(100, 100, QImage::Format_Indexed8);

  m_difftagimage = QImage(100, 100, QImage::Format_ARGB32);
  m_difftagimageScaled = QImage(100, 100, QImage::Format_ARGB32);

  m_maskpad = QImage(100, 100, QImage::Format_ARGB32);
  m_mainusermask = QImage(10, 10, QImage::Format_ARGB32);
  m_usermask = QImage(100, 100, QImage::Format_ARGB32);
  m_prevusermask = QImage(100, 100, QImage::Format_ARGB32);
  m_usermaskScaled = QImage(100, 100, QImage::Format_ARGB32);
  m_maskpadScaled = QImage(100, 100, QImage::Format_ARGB32);

  m_slice = 0;
  m_sliceImage = 0;
  m_maskslice = 0;
  m_tags = 0;
  m_prevtags = 0;

  m_currSlice = 0;
  m_minDSlice = m_maxDSlice = 0;
  m_minWSlice = m_maxWSlice = 0;
  m_minHSlice = m_maxHSlice = 0;

  m_cursorPos = QPoint(0,0);
  m_pickPoint = false;
  m_pickDepth = 0;
  m_pickWidth = 0;
  m_pickHeight = 0;

  m_rubberBand = QRect(0,0,0,0);
  m_rubberXmin = false;
  m_rubberYmin = false;
  m_rubberXmax = false;
  m_rubberYmax = false;
  m_rubberNew = false;
  m_rubberBandActive = false;

  m_automaticTagging = false;
  m_propagateTagging = false;
  m_propDir = false;
  m_propSlice = 0;

  m_copyMaskTags = true;

  m_vgt.clear();

  updateTagColors();
}

void ImageWidget::enterEvent(QEvent* event) { setFocus(); }
void ImageWidget::leaveEvent(QEvent* event) { clearFocus(); }

void
ImageWidget::saveImage()
{
  QString imgFile = QFileDialog::getSaveFileName(0,
			 "Save composite image",
			 Global::previousDirectory(),
			 "Image Files (*.png *.tif *.bmp *.jpg)");

  if (imgFile.isEmpty())
    return;

  QImage sImage = m_image;
  QPainter p(&sImage);
  p.setCompositionMode(QPainter::CompositionMode_Overlay);
  p.drawImage(0,0, m_maskimage);

  sImage.save(imgFile);
  QMessageBox::information(0, "Save Image", "Done");
}

void
ImageWidget::setZoom(float z)
{
  setMinimumSize(QSize(m_imgWidth, m_imgHeight));

  m_zoom = qMax(0.01f, z);

  resizeImage();
  update();
}


void
ImageWidget::depthUserRange(int& umin, int& umax)
{
  umin = m_minDSlice;
  umax = m_maxDSlice;
}
void
ImageWidget::widthUserRange(int& umin, int& umax)
{
  umin = m_minWSlice;
  umax = m_maxWSlice;
}
void
ImageWidget::heightUserRange(int& umin, int& umax)
{
  umin = m_minHSlice;
  umax = m_maxHSlice;
}

void
ImageWidget::sliceChanged(int slc)
{
  if (m_sliceType == DSlice)
    m_currSlice = slc;
  else if (m_sliceType == WSlice)
    m_currSlice = slc;
  else
    m_currSlice = slc;

  emit getSlice(m_currSlice);
}

void
ImageWidget::userRangeChanged(int umin, int umax)
{
  if (m_sliceType == DSlice)
    {
      m_minDSlice = umin;
      m_maxDSlice = umax;
    }
  else if (m_sliceType == WSlice)
    {
      m_minWSlice = umin;
      m_maxWSlice = umax;
    }
  else
    {
      m_minHSlice = umin;
      m_maxHSlice = umax;
    }
    
  update();
}


void
ImageWidget::loadLookupTable(QImage colorMap)
{
  QImage lutImage = colorMap.mirrored(false, true);
  uchar *bits = lutImage.bits();
  memcpy(m_lut, bits, 4*256*256);

  Global::setLut(m_lut);

  recolorImage();
  resizeImage();
  update();
}

void
ImageWidget::setGridSize(int d, int w, int h)
{
  m_Depth = qMax(0, d-1);
  m_Width = qMax(0, w-1);
  m_Height = qMax(0, h-1);
   
  m_minDSlice = 0;
  m_maxDSlice = m_Depth;
  m_minWSlice = 0;
  m_maxWSlice = m_Width;
  m_minHSlice = 0;
  m_maxHSlice = m_Height;
}

void
ImageWidget::setSliceType(int st)
{
  m_sliceType = st;
  if (m_sliceType == DSlice)
    m_currSlice = m_minDSlice;
  else if (m_sliceType == WSlice)
    m_currSlice = m_minWSlice;
  else
    m_currSlice = m_minHSlice;


  //---------------------------------
  int wd, ht;
  if (m_sliceType == DSlice)
    {
      wd = m_Height+1;
      ht = m_Width+1;
    }
  else if (m_sliceType == WSlice)
    {
      wd = m_Height+1;
      ht = m_Depth+1;
    }
  else
    {
      wd = m_Width+1;
      ht = m_Depth+1;
    }

  if (m_tags) delete [] m_tags;
  m_tags = new uchar[wd*ht];
  memset(m_tags, 0, wd*ht);

  if (m_prevtags) delete [] m_prevtags;
  m_prevtags = new uchar[wd*ht];
  memset(m_prevtags, 0, wd*ht);
  //---------------------------------


  //---------------------------------
  // update rubberband extents
  float left, right, top, bottom;

  float width, height;
  if (m_sliceType == DSlice)
    {
      left = (float)m_minHSlice/(float)m_Height;
      right = (float)m_maxHSlice/(float)m_Height;
      top = (float)m_minWSlice/(float)m_Width;
      bottom = (float)m_maxWSlice/(float)m_Width;

      width = m_Height;
      height = m_Width;
    }
  else if (m_sliceType == WSlice)
    {
      left = (float)m_minHSlice/(float)m_Height;
      right = (float)m_maxHSlice/(float)m_Height;
      top = (float)m_minDSlice/(float)m_Depth;
      bottom = (float)m_maxDSlice/(float)m_Depth;

      width = m_Height;
      height = m_Depth;
    }
  else
    {
      left = (float)m_minWSlice/(float)m_Width;
      right = (float)m_maxWSlice/(float)m_Width;
      top = (float)m_minDSlice/(float)m_Depth;
      bottom = (float)m_maxDSlice/(float)m_Depth;

      width = m_Width;
      height = m_Depth;
    }

  left = qBound(0.0f, left, 1.0f);
  top = qBound(0.0f, top, 1.0f);
  right = qBound(0.0f, right, 1.0f);
  bottom = qBound(0.0f, bottom, 1.0f);

  m_rubberBand.setLeft(left);
  m_rubberBand.setTop(top);
  m_rubberBand.setRight(right);
  m_rubberBand.setBottom(bottom);
  //---------------------------------
 
  // reset zoom
  QWidget *prt = (QWidget*)parent();
  int frmHeight = prt->rect().height()-50;
  int frmWidth = prt->rect().width()-50;
  float rH = (float)frmHeight/(float)height;
  float rW = (float)frmWidth/(float)width;
  m_zoom = qMin(rH, rW);
  
  emit getSlice(m_currSlice);
}

void
ImageWidget::updateTagColors()
{
  uchar *tagColors = Global::tagColors();

  m_tagColors.clear();
  m_tagColors.resize(256);

  for(int i=0; i<256; i++)
    {
      uchar r = tagColors[4*i+0];
      uchar g = tagColors[4*i+1];
      uchar b = tagColors[4*i+2];
      m_tagColors[i] = qRgba(r, g, b, 127);
    }

  m_maskimage.setColorTable(m_tagColors);

  m_maskimageScaled = m_maskimage.scaled(m_simgWidth,
					 m_simgHeight,
					 Qt::IgnoreAspectRatio,
					 Qt::SmoothTransformation);  
  update();
}

void
ImageWidget::setImage(uchar *slice, uchar *mask)
{
  if (m_slice) delete [] m_slice;
  if (m_sliceImage) delete [] m_sliceImage;
  if (m_maskslice) delete [] m_maskslice;
  

  if (m_sliceType == DSlice)
    {
      m_imgWidth = m_Height+1;
      m_imgHeight = m_Width+1;
    }
  else if (m_sliceType == WSlice)
    {
      m_imgWidth = m_Height+1;
      m_imgHeight = m_Depth+1;
    }
  else
    {
      m_imgWidth = m_Width+1;
      m_imgHeight = m_Depth+1;
    }

  m_slice = new uchar[2*m_imgWidth*m_imgHeight];
  memcpy(m_slice, slice, 2*m_imgWidth*m_imgHeight);

  m_maskslice = new uchar[m_imgWidth*m_imgHeight];
  memcpy(m_maskslice, mask, m_imgWidth*m_imgHeight);

  m_sliceImage = new uchar[4*m_imgWidth*m_imgHeight];

  recolorImage();

  if (m_mainusermask.width() != m_imgWidth ||
      m_mainusermask.height() != m_imgHeight)
    {
      m_mainusermask = QImage(m_imgWidth,m_imgHeight, QImage::Format_ARGB32);
      m_mainusermask.fill(0);
    }

  if (m_copyMaskTags)
    copyMaskTags(mask);
  else
    memcpy(m_tags, m_maskslice, m_imgWidth*m_imgHeight);

  memcpy(m_prevtags, m_tags, m_imgWidth*m_imgHeight);

  calcDiffTags();

  m_prevusermask = m_mainusermask;
  maskMask(&m_prevusermask);

  m_maskpad = QImage(m_imgWidth,m_imgHeight, QImage::Format_ARGB32);
  m_maskpad.fill(0);
  m_usermask = QImage(m_imgWidth,m_imgHeight, QImage::Format_ARGB32);
  m_usermask.fill(0);

  if (m_prevusermask.height() == m_maskpad.height() &&
      m_prevusermask.width() == m_maskpad.width())
    {
      m_maskpad = m_prevusermask;
      addImage();
    }

  resizeImage();
  
  if (m_propagateTagging)
    tagSlice();

  update();
}

void
ImageWidget::calcDiffTags()
{
  if (m_difftagimage.width() != m_imgWidth ||
      m_difftagimage.height() != m_imgHeight)
    m_difftagimage = QImage(m_imgWidth,m_imgHeight, QImage::Format_ARGB32);
  m_difftagimage.fill(0);

  for(int j=0; j<m_imgHeight; j++)
    for(int i=0; i<m_imgWidth; i++)
      {
	int idx = j*m_imgWidth + i;
	if (m_tags[idx] != m_maskslice[idx])
	  m_difftagimage.setPixel(i, j, Qt::white);
      }

  m_difftagimageScaled = m_difftagimage.scaled(m_simgWidth,
					       m_simgHeight,
					       Qt::IgnoreAspectRatio,
					       Qt::SmoothTransformation);
}

void
ImageWidget::copyMaskTags(uchar *mask)
{
  int ist, ied, jst, jed;
  if (m_sliceType == DSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minWSlice;
      jed = m_maxWSlice;
    }
  else if (m_sliceType == WSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }
  else
    {
      ist = m_minWSlice;
      ied = m_maxWSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }

  for(int j=jst; j<=jed; j++)
    for(int i=ist; i<=ied; i++)
      {
	int sidx = j*m_imgWidth + i;
	if (m_sliceImage[4*sidx+3] > 0)
	  {
	    int tg = mask[sidx];
	    if (tg == 0)
	      tg = m_tags[sidx];
//	    uchar r = m_tagColors[4*tg+0];
//	    uchar g = m_tagColors[4*tg+1];
//	    uchar b = m_tagColors[4*tg+2];
//	    m_mainusermask.setPixel(i, j, qRgba(r, g, b, 200));
	    
	    m_tags[sidx] = tg;
	  }
	else
	  m_tags[sidx] = 0;
      }
}

void
ImageWidget::recolorImage()
{

  for(int i=0; i<m_imgHeight*m_imgWidth; i++)
    {
      uchar v = m_slice[2*i];
      uchar g = m_slice[2*i+1];

      int idx = 4*(256*g + v);
      m_sliceImage[4*i+0] = m_lut[idx+0];
      m_sliceImage[4*i+1] = m_lut[idx+1];
      m_sliceImage[4*i+2] = m_lut[idx+2];
      m_sliceImage[4*i+3] = m_lut[idx+3];
    }

  m_image = QImage(m_sliceImage,
		   m_imgWidth,
		   m_imgHeight,
		   QImage::Format_RGB32);
  

  m_maskimage = QImage(m_maskslice,
		       m_imgWidth,
		       m_imgHeight,
		       m_imgWidth,
		       QImage::Format_Indexed8);
  m_maskimage.setColorTable(m_tagColors);
}

void
ImageWidget::setRawValue(QList<uchar> vgt)
{
  m_vgt = vgt;
  update();
}

void
ImageWidget::resizeImage()
{
  setMinimumSize(QSize(m_zoom*m_imgWidth + 20,
		       m_zoom*m_imgHeight + 40));

  setMaximumSize(QSize(m_zoom*m_imgWidth + 20,
		       m_zoom*m_imgHeight + 40));


  int frmWidth = m_zoom*m_imgWidth;
  int frmHeight = m_zoom*m_imgHeight;

  float rH = (float)m_imgHeight/(float)frmHeight;
  float rW = (float)m_imgWidth/(float)frmWidth;

  m_simgHeight = m_imgHeight;
  m_simgWidth = m_imgWidth;
  //if (rH > 1 || rW > 1)
    {
      // scaledown the image
      if (rH > rW)
	{
	  m_simgHeight = m_imgHeight/rH;
	  m_simgWidth = m_imgWidth/rH;
	}
      else
	{
	  m_simgHeight = m_imgHeight/rW;
	  m_simgWidth = m_imgWidth/rW;
	}
    }

  m_imageScaled = m_image.scaled(m_simgWidth,
				 m_simgHeight,
				 Qt::IgnoreAspectRatio,
				 Qt::SmoothTransformation);
  
  m_maskimageScaled = m_maskimage.scaled(m_simgWidth,
					 m_simgHeight,
					 Qt::IgnoreAspectRatio,
					 Qt::SmoothTransformation);
  

  m_usermaskScaled = m_usermask.scaled(m_simgWidth,
				       m_simgHeight,
				       Qt::IgnoreAspectRatio,
				       Qt::SmoothTransformation);
  
  m_maskpadScaled = m_maskpad.scaled(m_simgWidth,
				     m_simgHeight,
				     Qt::IgnoreAspectRatio,
				     Qt::SmoothTransformation);
  
}

void
ImageWidget::resizeEvent(QResizeEvent *event)
{
  resizeImage();
  update();
}

void
ImageWidget::drawSizeText(QPainter *p)
{  
  p->setPen(QPen(Qt::black, 1));

  QString txt = QString("%1 %2 %3 [%4:%5 %6:%7 %8:%9] (%10 %11 %12)").\
    arg(m_Height+1).arg(m_Width+1).arg(m_Depth+1).		      \
    arg(m_minHSlice).arg(m_maxHSlice).				      \
    arg(m_minWSlice).arg(m_maxWSlice).				      \
    arg(m_minDSlice).arg(m_maxDSlice).				      \
    arg(m_maxHSlice-m_minHSlice+1).				      \
    arg(m_maxWSlice-m_minWSlice+1).				      \
    arg(m_maxDSlice-m_minDSlice+1);				      \

  txt += QString("   (radius = %1)").arg(Global::spread());

  p->drawText(m_simgX, m_simgY-3, txt);
}

void
ImageWidget::paintEvent(QPaintEvent *event)
{
  QPainter p(this);

  //p.setCompositionMode(QPainter::CompositionMode_Overlay);
  p.drawImage(m_simgX, m_simgY, m_imageScaled);
  p.drawImage(m_simgX, m_simgY, m_maskimageScaled);
  p.drawImage(m_simgX, m_simgY, m_difftagimageScaled);
  //p.drawImage(m_simgX, m_simgY, m_usermaskScaled);
  if (m_areaTool)
    p.drawImage(m_simgX, m_simgY, m_maskpadScaled);


  p.setCompositionMode(QPainter::CompositionMode_SourceOver);

  drawSizeText(&p);

  drawRubberBand(&p);

  if (m_pickPoint)
    drawRawValue(&p);

  if (!m_areaTool && !m_rubberBandActive)
    {
      int rad = Global::spread()*(float)m_simgWidth/(float)m_imgWidth;
      p.setPen(Qt::white);
      p.setBrush(QColor(150, 0, 0, 150));
      p.drawEllipse(m_cursorPos.x()-rad,
		    m_cursorPos.y()-rad,
		    2*rad, 2*rad);
    }

  if (hasFocus())
    {
      p.setPen(QPen(QColor(250, 100, 0), 2));
      p.setBrush(Qt::transparent);
      p.drawRect(rect());
    }

  checkPropagateTagging();
}

void
ImageWidget::checkPropagateTagging()
{
  if (m_propagateTagging)
    {
      if (m_propDir) // go down tagging slices
	{
	  if ((m_sliceType == DSlice && m_currSlice >= m_maxDSlice) ||
	      (m_sliceType == WSlice && m_currSlice >= m_maxWSlice) ||
	      (m_sliceType == HSlice && m_currSlice >= m_maxHSlice))
	    {
	      m_propDir = false;
	      m_currSlice = m_propSlice;
	    }
	  else
	    {
	      m_currSlice ++;
	      emit getSlice(m_currSlice);
	    }
	}

      if (!m_propDir) // go up tagging slices
	{
	  if ((m_sliceType == DSlice && m_currSlice <= m_minDSlice) ||
	      (m_sliceType == WSlice && m_currSlice <= m_minWSlice) ||
	      (m_sliceType == HSlice && m_currSlice <= m_minHSlice))
	    {
	      m_automaticTagging = false;
	      m_propagateTagging = false;
	      emit getSlice(m_currSlice); // just to update the image
	    }
	  else
	    {
	      m_currSlice --;
	      emit getSlice(m_currSlice);
	    }
	}
    }
}

void
ImageWidget::drawRubberBand(QPainter *p)
{
  int x = m_simgX + m_simgWidth*m_rubberBand.left();
  int y = m_simgY + m_simgHeight*m_rubberBand.top();
  int width = m_simgWidth*m_rubberBand.width();
  int height = m_simgHeight*m_rubberBand.height();

  p->setBrush(Qt::transparent);

  p->setPen(QPen(Qt::white, 1));
  p->drawRect(x, y, width, height);


  //-------------------------------------------------
  // slightly blacken region outside the bounding box
  p->setPen(Qt::transparent);
  p->setBrush(QColor(0, 0, 0, 100));

  int bottom = m_simgHeight*m_rubberBand.bottom();
  int right = m_simgWidth*m_rubberBand.right();
      
  p->drawRect(m_simgX, m_simgY, m_simgWidth, y-m_simgY);
  p->drawRect(m_simgX, m_simgY+bottom,
	      m_simgWidth, m_simgHeight-bottom);

  height = height + (m_simgY+bottom - (y+height));
  p->drawRect(m_simgX, y, x-m_simgX, height);
  p->drawRect(m_simgX+right, y,
	      m_simgWidth-right, height);
  //-------------------------------------------------


  if (m_rubberBandActive)
    {
      int x0,x1,y0,y1;
      if (m_rubberXmin)
	{
	  x1 = x0 = x;
	  y0 = y;
	  y1 = y+height;
	}
      else if (m_rubberXmax)
	{
	  x1 = x0 = x+width;
	  y0 = y;
	  y1 = y+height;
	}
      else if (m_rubberYmin)
	{
	  x0 = x;
	  y1 = y0 = y;
	  x1 = x+width;
	}
      else if (m_rubberYmax)
	{
	  x0 = x;
	  y1 = y0 = y+height;
	  x1 = x+width;
	}
      p->setPen(QPen(Qt::white, 3));
      p->drawLine(x0, y0, x1, y1);
      p->setPen(QPen(Qt::black, 1));
      p->drawLine(x0, y0, x1, y1);
    }
}

void
ImageWidget::drawRawValue(QPainter *p)
{
  if (m_vgt.count() < 3)
    return;

  QString str;
  int xp = m_cursorPos.x();
  int yp = m_cursorPos.y();

  str = QString("%1 %2 %3").\
          arg(m_pickHeight).\
          arg(m_pickWidth).\
          arg(m_pickDepth);
    
  str += QString(" vg(%1,%2) t(%3)").\
	     arg(m_vgt[0]).\
	     arg(m_vgt[1]).\
	     arg(m_vgt[2]);

  QFont pfont = QFont("Helvetica", 10);
  QPainterPath pp;
  pp.addText(QPointF(0,0), 
	     pfont,
	     str);
  QRectF br = pp.boundingRect();
  float by = br.height()/2;      
  float bw = br.width();      

  int x = xp-bw-30;
  int xe = xp-20;
  if (x < 1)
    {
      x = xp+20;
      xe = x;
    }

  p->setPen(Qt::darkRed);
  p->setBrush(QColor(0,0,0,200));
  p->drawRect(x, yp-by,
	      bw+10, 2*by+5);
  p->setPen(Qt::white);
  p->drawText(x+5, yp+by,
	      str);
  p->setPen(Qt::black);
  p->drawLine(xe, yp, xp, yp);  
}

void
ImageWidget::keyPressEvent(QKeyEvent *event)
{
  int shiftModifier = event->modifiers() & Qt::ShiftModifier;
  int ctrlModifier = event->modifiers() & Qt::ControlModifier;

  if (ctrlModifier)
    {
      if (event->key() == Qt::Key_0)
	setZoom(1);
      else if (event->key() == Qt::Key_Plus ||
	       event->key() == Qt::Key_Equal)
	setZoom(m_zoom+0.1);
      else if (event->key() == Qt::Key_Underscore ||
	       event->key() == Qt::Key_Minus)
	setZoom(m_zoom-0.1);

      return;
    }

  if (event->key() == Qt::Key_S &&
      (event->modifiers() & Qt::AltModifier) )
    {
      saveImage();
      return;
    } 

  if (event->key() == Qt::Key_A)
    {
      m_automaticTagging = true;
      m_propagateTagging = true;
      m_propSlice = m_currSlice;
      m_propDir = true;
      propagateTaggedRegion();
      emit getSlice(m_currSlice);
    }
  else if (event->key() == Qt::Key_C)
    {
      m_copyMaskTags = !m_copyMaskTags;
      emit getSlice(m_currSlice);
    }
  else if (event->key() == Qt::Key_P)
    {
      m_automaticTagging = false;
      m_propagateTagging = true;
      m_propSlice = m_currSlice;
      m_propDir = true;
      propagateTaggedRegion();
      emit getSlice(m_currSlice);
    }
  else if (event->key() == Qt::Key_I)
    {
      tagSlice();
      update();
    }
  else if (event->key() == Qt::Key_V)
    emit tagAllVisible(m_minDSlice, m_maxDSlice,
		       m_minWSlice, m_maxWSlice,
		       m_minHSlice, m_maxHSlice);
  else if (event->key() == Qt::Key_F)
    {
      memcpy(m_tags, m_prevtags, m_imgWidth*m_imgHeight);
      emitFill(false);
    }
  else if (event->key() == Qt::Key_T)
    {
      memcpy(m_tags, m_prevtags, m_imgWidth*m_imgHeight);
      emitFill(true);
      emit getSlice(m_currSlice);
    }
  else if (event->key() == Qt::Key_D)
    {
      if (shiftModifier)
	emit dilate();
      else // dilate only on the slice
	{
	  if (m_sliceType == DSlice)
	    emit dilate(m_currSlice, m_currSlice,
			m_minWSlice, m_maxWSlice,
			m_minHSlice, m_maxHSlice);
	  else if (m_sliceType == WSlice)
	    emit dilate(m_minDSlice, m_maxDSlice,
			m_currSlice, m_currSlice,
			m_minHSlice, m_maxHSlice);
	  else
	    emit dilate(m_minDSlice, m_maxDSlice,
			m_minWSlice, m_maxWSlice,
			m_currSlice, m_currSlice);
	}
    }
  else if (event->key() == Qt::Key_E)
    {
      if (shiftModifier)
	emit erode();
      else // erode only on the slice
	{
	  if (m_sliceType == DSlice)
	    emit erode(m_currSlice, m_currSlice,
		       m_minWSlice, m_maxWSlice,
		       m_minHSlice, m_maxHSlice);
	  else if (m_sliceType == WSlice)
	    emit erode(m_minDSlice, m_maxDSlice,
		       m_currSlice, m_currSlice,
		       m_minHSlice, m_maxHSlice);
	  else
	    emit erode(m_minDSlice, m_maxDSlice,
		       m_minWSlice, m_maxWSlice,
		       m_currSlice, m_currSlice);
	}
    }
  else if (event->key() == Qt::Key_Escape)
    {
      if (m_rubberBandActive)
	{
	  m_rubberBand.setLeft(0);
	  m_rubberBand.setTop(0);
	  m_rubberBand.setRight(1.0);
	  m_rubberBand.setBottom(1.0);
	  update();
	}
      else
	{
	  if (m_propagateTagging)
	    {
	      m_propagateTagging = false;
	      m_automaticTagging = false;
	    }
	  else
	    {
	      m_mainusermask.fill(0);
	      m_usermask.fill(0);
	      m_usermaskScaled.fill(0);
	      m_maskpad.fill(0);
	      memcpy(m_tags, m_maskslice, m_imgWidth*m_imgHeight);
	      memcpy(m_prevtags, m_maskslice, m_imgWidth*m_imgHeight);
	      generateMaskImage();
	      update();
	    }
	}
    }
  else if (event->key() == Qt::Key_Up)
    {
      int rad = Global::spread();
      Global::setSpread(rad+1);

      if (m_areaTool)
	preselect();
      else
	update();
    }
  else if (event->key() == Qt::Key_Down)
    {
      int rad = Global::spread();
      Global::setSpread(rad-1);

      if (m_areaTool)
	preselect();
      else
	update();
    }
  else if (event->key() == Qt::Key_Right)
    {
      if (shiftModifier)
	{
	  int dg = Global::deltaG()+1;
	  emit setDeltaG(dg);
	}
      else
	{
	  int dv = Global::deltaV()+1;
	  emit setDeltaV(dv);
	}

      if (m_areaTool)
	preselect();
      else
	update();
    }
  else if (event->key() == Qt::Key_Left)
    {
      if (shiftModifier)
	{
	  int dg = Global::deltaG()-1;
	  emit setDeltaG(dg);
	}
      else
	{
	  int dv = Global::deltaV()-1;
	  emit setDeltaV(dv);
	}

      if (m_areaTool)
	preselect();
      else
	update();
    }
//  else if (event->key() == Qt::Key_Space)
//    {
//      bool ok;
//      QString cmd;
//      QString str;
//      str = "Enter new Min and Max Slider limits\n";
//      cmd = QInputDialog::getText(0,
//				  "Slider Min and Max",
//				  str,
//				  QLineEdit::Normal,
//				  "",
//				  &ok);
//      if (ok && !cmd.isEmpty())
//	processCommands(cmd);
//    }
  else if (event->key() == Qt::Key_S)
    emit saveProject();
}

bool
ImageWidget::checkRubberBand(int xpos, int ypos,
			    bool shiftModifier)
{
  m_rubberXmin = false;
  m_rubberYmin = false;
  m_rubberXmax = false;
  m_rubberYmax = false;
  m_rubberNew = false;
  m_rubberBandActive = false;

  if (xpos >= m_simgX-2 && xpos <= m_simgX+m_simgWidth+2 &&
      ypos >= m_simgY-2 && ypos <= m_simgY+m_simgHeight+2)
    {
      // give some slack (2px) at the ends
      int rxmin = m_simgX + m_simgWidth*m_rubberBand.left();
      int rxmax = m_simgX + m_simgWidth*m_rubberBand.right();
      int rymin = m_simgY + m_simgHeight*m_rubberBand.top();
      int rymax = m_simgY + m_simgHeight*m_rubberBand.bottom();
      
//      if (qAbs(xpos-rxmin) < 10)
//	m_rubberXmin = true;
//      else if (qAbs(xpos-rxmax) < 10)
//	m_rubberXmax = true;
//      else if (qAbs(ypos-rymin) < 10)
//	m_rubberYmin = true;
//      else if (qAbs(ypos-rymax) < 10)
//	m_rubberYmax = true;
      if (xpos-rxmin < 0 && xpos-rxmin > -10)
	m_rubberXmin = true;
      else if (xpos-rxmax > 0 && xpos-rxmax < 10)
	m_rubberXmax = true;
      else if (ypos-rymin < 0 && ypos-rymin > -10)
	m_rubberYmin = true;
      else if (ypos-rymax > 0 && ypos-rymax < 10)
	m_rubberYmax = true;
      else if (shiftModifier)
	{	  
	  m_rubberNew = true;
	  
	  float frcX = (float)(xpos-m_simgX)/(float)m_simgWidth;
	  float frcY = (float)(ypos-m_simgY)/(float)m_simgHeight;	  
	  frcX = qBound(0.0f, frcX, 1.0f);
	  frcY = qBound(0.0f, frcY, 1.0f);
	  float wd = 1.0/m_imgWidth;
	  float ht = 1.0/m_imgHeight;
	  m_rubberBand = QRectF(frcX,frcY,wd,ht);
	}
    }

  m_rubberBandActive =   ( m_rubberXmin |
			   m_rubberYmin |
			   m_rubberXmax |
			   m_rubberYmax |
			   m_rubberNew );

  return m_rubberBandActive;
}

void
ImageWidget::mousePressEvent(QMouseEvent *event)
{
  QPoint pp = mapFromParent(event->pos());
  float ypos = pp.y();
  float xpos = pp.x();
  
  m_button = event->button();

  m_cursorPos = pp;
  m_pickPoint = false;

  bool shiftModifier = event->modifiers() & Qt::ShiftModifier;

  if (m_button == Qt::LeftButton)
    {
      if (validPickPoint(xpos, ypos))
	{
	  if (checkRubberBand(xpos, ypos, shiftModifier))
	    return;

	  if (m_areaTool)
	    addImage();
	  else
	    {
	      if (m_sliceType == DSlice)
		dotImage(m_pickHeight,
			 m_pickWidth);
	      else if (m_sliceType == WSlice)
		dotImage(m_pickHeight,
			 m_pickDepth);
	      else
		dotImage(m_pickWidth,
			 m_pickDepth);
	    }
	  update();
	  return;
	}
    }
  else if (m_button == Qt::RightButton)
    {
      if (validPickPoint(xpos, ypos))
	{
	  bool shiftModifier = event->modifiers() & Qt::ShiftModifier;
	  if (shiftModifier)
	    {
	      m_pickPoint = true;
	      emit getRawValue(m_pickDepth,
			       m_pickWidth,
			       m_pickHeight);
	    }
	  else
	    {
	      // update user mask image
	      if (m_areaTool)
		{
		  if (m_sliceType == DSlice)
		    removeImage();
		  else if (m_sliceType == WSlice)
		    removeImage();
		  else
		    removeImage();
		}		  
	      else
		{
		  if (m_sliceType == DSlice)
		    removeDotImage(m_pickHeight,
				   m_pickWidth);
		  else if (m_sliceType == WSlice)
		    removeDotImage(m_pickHeight,
				   m_pickDepth);
		  else
		    removeDotImage(m_pickWidth,
				   m_pickDepth);
		}
	    }
	}
    }

  update();
}

void
ImageWidget::updateRubberBand(int xpos, int ypos)
{
  if (m_rubberNew)
    {
      float frcX = (float)(xpos-m_simgX)/(float)m_simgWidth;
      float frcY = (float)(ypos-m_simgY)/(float)m_simgHeight;	  
      frcX = qBound(0.0f, frcX, 1.0f);
      frcY = qBound(0.0f, frcY, 1.0f);
      float left = m_rubberBand.left();
      float right = frcX;
      float top = m_rubberBand.top();
      float bottom = frcY;
      m_rubberBand = QRectF(left, top, right-left, bottom-top);
    }
  else if (m_rubberXmin)
    {
      float frcX = (float)(xpos-m_simgX)/(float)m_simgWidth;
      frcX = qBound(0.0f, frcX, 1.0f);
      m_rubberBand.setLeft(frcX);
      int w = m_imgWidth*(m_rubberBand.right()-m_rubberBand.left());
      if (w < 10)
	{
	  float delta = 10.0/m_imgWidth;
	  delta = m_rubberBand.right()-delta;
	  delta = qBound(0.0f, delta, 1.0f);
	  m_rubberBand.setLeft(delta);
	}
    }
  else if (m_rubberXmax)
    {
      float frcX = (float)(xpos-m_simgX)/(float)m_simgWidth;
      frcX = qBound(0.0f, frcX, 1.0f);
      m_rubberBand.setRight(frcX);
      int w = m_imgWidth*(m_rubberBand.right()-m_rubberBand.left());
      if (w < 10)
	{
	  float delta = 10.0/m_imgWidth;
	  delta = m_rubberBand.left()+delta;
	  delta = qBound(0.0f, delta, 1.0f);
	  m_rubberBand.setRight(delta);
	}
    }
  else if (m_rubberYmin)
    {
      float frcY = (float)(ypos-m_simgY)/(float)m_simgHeight;	  
      frcY = qBound(0.0f, frcY, 1.0f);
      m_rubberBand.setTop(frcY);
      int h = m_imgHeight*(m_rubberBand.bottom()-m_rubberBand.top());
      if (h < 10)
	{
	  float delta = 10.0/m_imgHeight;
	  delta = m_rubberBand.bottom()-delta;
	  delta = qBound(0.0f, delta, 1.0f);
	  m_rubberBand.setTop(delta);
	}
    }
  else if (m_rubberYmax)
    {
      float frcY = (float)(ypos-m_simgY)/(float)m_simgHeight;	  
      frcY = qBound(0.0f, frcY, 1.0f);
      m_rubberBand.setBottom(frcY);
      int h = m_imgHeight*(m_rubberBand.bottom()-m_rubberBand.top());
      if (h < 10)
	{
	  float delta = 10.0/m_imgHeight;
	  delta = m_rubberBand.top()+delta;
	  delta = qBound(0.0f, delta, 1.0f);
	  m_rubberBand.setBottom(delta);
	}
    }
}

bool
ImageWidget::validPickPoint(int xpos, int ypos)
{
  if (! m_slice)
    return false;

  if (xpos >= m_simgX-2 && xpos <= m_simgX+m_simgWidth+2 &&
      ypos >= m_simgY-2 && ypos <= m_simgY+m_simgHeight+2)
    {
      // give some slack(2px) at the ends
      float frcX = (float)(xpos-m_simgX)/(float)m_simgWidth;
      float frcY = (float)(ypos-m_simgY)/(float)m_simgHeight;
      
      frcX = qBound(0.0f, frcX, 1.0f);
      frcY = qBound(0.0f, frcY, 1.0f);
      
      if (m_sliceType == DSlice)
	{
	  m_pickDepth = m_currSlice;
	  m_pickHeight = frcX*m_imgWidth;
	  m_pickWidth = frcY*m_imgHeight;
	}
      else if (m_sliceType == WSlice)
	{
	  m_pickWidth = m_currSlice;
	  m_pickHeight = frcX*m_imgWidth;
	  m_pickDepth = frcY*m_imgHeight;
	}
      else
	{
	  m_pickHeight = m_currSlice;
	  m_pickWidth = frcX*m_imgWidth;
	  m_pickDepth = frcY*m_imgHeight;
	}

      return true;
    }
  return false;
}

void
ImageWidget::preselect()
{
  float ypos = m_cursorPos.y();
  float xpos = m_cursorPos.x();


  if (validPickPoint(xpos, ypos))
    {
      if (checkRubberBand(xpos, ypos, false))
	{
	  update();
	  return;
	}

      if (m_areaTool)
	{
	  if (m_sliceType == DSlice)
	    calcImage(m_pickHeight,
		      m_pickWidth);
	  else if (m_sliceType == WSlice)
	    calcImage(m_pickHeight,
		      m_pickDepth);
	  else
	    calcImage(m_pickWidth,
		      m_pickDepth);
	}
      update();
    }
}

void
ImageWidget::mouseMoveEvent(QMouseEvent *event)
{
  if (!hasFocus())
    setFocus();

  QPoint pp = mapFromParent(event->pos());
  float ypos = pp.y();
  float xpos = pp.x();

  m_cursorPos = pp;

  if (event->buttons() == Qt::NoButton)
    {
      m_pickPoint = false;
      preselect();
    }
  else if (m_button == Qt::LeftButton)
    {
      if (m_rubberBandActive)
	{
	  updateRubberBand(xpos, ypos);
	  update();
	}
      else if (!m_areaTool)
	{	
	  if (validPickPoint(xpos, ypos))
	    {
	      if (m_sliceType == DSlice)
		dotImage(m_pickHeight,
			 m_pickWidth);
	      else if (m_sliceType == WSlice)
		dotImage(m_pickHeight,
			 m_pickDepth);
	      else
		dotImage(m_pickWidth,
			 m_pickDepth);
	      
	      update();
	    }
	}
    }
  else if (m_button == Qt::RightButton)
    {
      if (validPickPoint(xpos, ypos))
	{
	  if (!m_areaTool)
	    {
	      if (m_sliceType == DSlice)
		removeDotImage(m_pickHeight,
			       m_pickWidth);
	      else if (m_sliceType == WSlice)
		removeDotImage(m_pickHeight,
			       m_pickDepth);
	      else
		removeDotImage(m_pickWidth,
			       m_pickDepth);
	    }
	  else
	    {
	      m_pickPoint = true;
	      emit getRawValue(m_pickDepth,
			       m_pickWidth,
			       m_pickHeight);
	    }
	  update();
	}
    }

  
//  if (m_pickPoint)
//    {
//      m_pickPoint = false;
//      update();
//    }
}

void
ImageWidget::mouseReleaseEvent(QMouseEvent *event)
{
  m_pickPoint = false;

  if (m_rubberBandActive)
    {
      float left = m_rubberBand.left();
      float right = m_rubberBand.right();
      float top = m_rubberBand.top();
      float bottom = m_rubberBand.bottom();
      float width = m_rubberBand.width();
      float height = m_rubberBand.height();
      if (width < 0)
	{
	  m_rubberBand.setLeft(right);
	  m_rubberBand.setRight(left);
	}
      if (height < 0)
	{
	  m_rubberBand.setTop(bottom);
	  m_rubberBand.setBottom(top);
	}

      // --- now update the min and max limits
      left = m_rubberBand.left();
      right = m_rubberBand.right();
      top = m_rubberBand.top();
      bottom = m_rubberBand.bottom();
      if (m_sliceType == DSlice)
	{
	  m_minHSlice = left*m_Height;
	  m_maxHSlice = right*m_Height;
	  m_minWSlice = top*m_Width;
	  m_maxWSlice = bottom*m_Width;
	}
      else if (m_sliceType == WSlice)
	{
	  m_minHSlice = left*m_Height;
	  m_maxHSlice = right*m_Height;
	  m_minDSlice = top*m_Depth;
	  m_maxDSlice = bottom*m_Depth;
	}
      else
	{
	  m_minWSlice = left*m_Width;
	  m_maxWSlice = right*m_Width;
	  m_minDSlice = top*m_Depth;
	  m_maxDSlice = bottom*m_Depth;
	}
    }

  m_rubberXmin = false;
  m_rubberYmin = false;
  m_rubberXmax = false;
  m_rubberYmax = false;
  m_rubberNew = false;
  m_rubberBandActive = false;

  update();
}


void
ImageWidget::wheelEvent(QWheelEvent *event)
{
  int minS, maxS;
  if (m_sliceType == DSlice)
    { 
      minS = m_minDSlice; 
      maxS = m_maxDSlice; 
    } 
  else if (m_sliceType == WSlice) 
    { 
      minS = m_minWSlice; 
      maxS = m_maxWSlice; 
    } 
  else 
    { 
      minS = m_minHSlice; 
      maxS = m_maxHSlice; 
    }  

  int numSteps = event->delta()/8.0f/15.0f;
  m_currSlice -= numSteps;
  m_currSlice = qBound(minS, m_currSlice, maxS);
  emit getSlice(m_currSlice);
  update();
}

void
ImageWidget::processCommands(QString cmd)
{
  cmd = cmd.toLower();
  QStringList list = cmd.split(" ", QString::SkipEmptyParts);

  if (list.size() == 2)
    {
      int smin = list[0].toInt();
      int smax = list[1].toInt();

      if (smin > smax)
	{ // swap
	  int t = smin;
	  smin = smax;
	  smax = t;
	}

      if (m_sliceType == DSlice)
	{
	  m_minDSlice = qBound(0, smin, m_Depth);
	  m_maxDSlice = qBound(0, smax, m_Depth);
	  m_currSlice = qBound(m_minDSlice,
			       m_currSlice,
			       m_maxDSlice);
	}
      else if (m_sliceType == WSlice)
	{
	  m_minWSlice = qBound(0, smin, m_Width);
	  m_maxWSlice = qBound(0, smax, m_Width);
	  m_currSlice = qBound(m_minWSlice,
			       m_currSlice,
			       m_maxWSlice);
	}
      else if (m_sliceType == HSlice)
	{
	  m_minHSlice = qBound(0, smin, m_Height);
	  m_maxHSlice = qBound(0, smax, m_Height);
	  m_currSlice = qBound(m_minHSlice,
			       m_currSlice,
			       m_maxHSlice);
	}

      update();
    }
  else
    QMessageBox::information(0, "Error",
			     "Please specify both the limits");

}

bool
ImageWidget::withinBounds(int scrx, int scry)
{
  if (m_sliceType == DSlice)
    {
      if (scrx >= m_minHSlice &&
	  scrx <= m_maxHSlice &&
	  scry >= m_minWSlice &&
	  scry <= m_maxWSlice)
	return true;
    }
  else if (m_sliceType == WSlice)
    {
      if (scrx >= m_minHSlice &&
	  scrx <= m_maxHSlice &&
	  scry >= m_minDSlice &&
	  scry <= m_maxDSlice)
	return true;
    }
  else
    {
      if (scrx >= m_minWSlice &&
	  scrx <= m_maxWSlice &&
	  scry >= m_minDSlice &&
	  scry <= m_maxDSlice)
	return true;
    }

  return false;
}

void
ImageWidget::calcImage(int scrx, int scry)
{
  m_maskpad.fill(0);

  if (! withinBounds(scrx, scry))
    return ;

  int sidx = scry*m_imgWidth + scrx;
  uchar v = m_slice[2*sidx];
  uchar g = m_slice[2*sidx+1];
  int lidx = 4*(256*g + v);

  if (m_lut[lidx+3] == 0)
    return;
  
  QPoint opt = QPoint(scrx, scry);

  QHash<uint, QPoint> hpt;
  uint optidx = opt.x()*5000 + opt.y();
  hpt[optidx] = opt;
  
  QStack<QPoint> stack;
  stack.push(opt);

  int drad2 = Global::spread()*Global::spread();

  int ov = v;
  int og = g;

  while (!stack.isEmpty())
    {
      QPoint pos = stack.pop();

      int imin = qMax(0, pos.x()-1);
      int jmin = qMax(0, pos.y()-1);
      int imax = qMin(m_imgWidth-1, pos.x()+1);
      int jmax = qMin(m_imgHeight-1, pos.y()+1);

      for(int j=jmin; j<=jmax; j++)
	for(int i=imin; i<=imax; i++)
	  {
	    if (withinBounds(i, j))
	      {
		QPoint pt(i,j);
		int ml = ( (pt.x()-opt.x())*(pt.x()-opt.x()) +
			   (pt.y()-opt.y())*(pt.y()-opt.y()) );
		// ptidx is used for hashing
		uint ptidx = pt.x()*5000 + pt.y();
		if (ml < drad2 &&
		    ! hpt.contains(ptidx))
		  {
		    int sidx = j*m_imgWidth+i;
		    int dv = m_slice[2*sidx];
		    int dg = m_slice[2*sidx + 1];
		    int lidx = 4*(256*dg + dv);
		    if (m_lut[lidx+3] > 0 &&
			qAbs(ov-dv) < Global::deltaV() &&
			dg < Global::deltaG())
		      {
			hpt[ptidx] = pt;
			stack.push(pt);
		      }
		  }
	      }
	  }
    }

  QList<QPoint> vpt;
  vpt = hpt.values();


  uchar *tagColors = Global::tagColors();
  int tg = Global::tag();
  uchar r = tagColors[4*tg+0];
  g = tagColors[4*tg+1];
  uchar b = tagColors[4*tg+2];
  uchar a = 200;

  for(int i=0; i<vpt.count(); i++)
    {
      int x = vpt[i].x();
      int y = vpt[i].y();

      m_maskpad.setPixel(x, y, qRgba(r,g,b,a));
    }



  // differentiate the edge
  const uchar *bits = m_maskpad.bits();
  for(int x=1; x<m_imgWidth-1; x++)
    for(int y=1; y<m_imgHeight-1; y++)
      {
	if (bits[4*(y*m_imgWidth+x)] > 0)
	  {
	    if (bits[4*((y-1)*m_imgWidth+(x-1))] == 0 ||
		bits[4*((y-1)*m_imgWidth+x)] == 0 ||
		bits[4*((y-1)*m_imgWidth+(x+1))] == 0 ||
		bits[4*(y*m_imgWidth+(x-1))] == 0 ||
		bits[4*(y*m_imgWidth+(x+1))] == 0 ||
		bits[4*((y+1)*m_imgWidth+(x-1))] == 0 ||
		bits[4*((y+1)*m_imgWidth+x)] == 0 ||
		bits[4*((y+1)*m_imgWidth+(x+1))] == 0)
	      m_maskpad.setPixel(x, y, qRgba(10, 200, 10, 200));
	  }	
      }  


  m_maskpadScaled = m_maskpad.scaled(m_simgWidth,
				     m_simgHeight,
				     Qt::IgnoreAspectRatio,
				     Qt::SmoothTransformation);
  
}

void
ImageWidget::maskMask(QImage *mask)
{
  mask->fill(0);
  const uchar *bits = m_mainusermask.bits();

  for(int i=0; i<m_imgWidth; i++)
    for(int j=0; j<m_imgHeight; j++)
      {
	int sidx = j*m_imgWidth + i;
	if (m_sliceImage[4*sidx+3] > 0 && bits[4*sidx] > 0)
	  {
	    uchar r = bits[4*sidx+0];
	    uchar g = bits[4*sidx+1];
	    uchar b = bits[4*sidx+2];
	    mask->setPixel(i, j, qRgba(r, g, b, 200));
	  }
      }
}

void
ImageWidget::dotImage(int x, int y)
{
  if (! withinBounds(x, y))
    return ;

  int ist, ied, jst, jed;
  if (m_sliceType == DSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minWSlice;
      jed = m_maxWSlice;
    }
  else if (m_sliceType == WSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }
  else
    {
      ist = m_minWSlice;
      ied = m_maxWSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }

  int xstart = qMax(ist, x-Global::spread());
  int xend = qMin(ied, x+Global::spread());
  int ystart = qMax(jst, y-Global::spread());
  int yend = qMin(jed, y+Global::spread());


  for(int i=xstart; i<xend; i++)
    for(int j=ystart; j<yend; j++)
      {
	int xi = qAbs(i-x);
	int yj = qAbs(j-y);
	float frc = ((float)(xi*xi + yj*yj)/
		     (float)(Global::spread()*Global::spread()));
	if (frc < 1)
	  {
	    int sidx = j*m_imgWidth + i;
	    if (m_sliceImage[4*sidx+3] > 0)
	      {
		//m_mainusermask.setPixel(i, j, qRgba(50, 200, 10, 200));
		int tg = Global::tag();
		uchar r = m_tagColors[4*tg+0];
		uchar g = m_tagColors[4*tg+1];
		uchar b = m_tagColors[4*tg+2];
		m_mainusermask.setPixel(i, j, qRgba(r, g, b, 200));

		m_prevtags[sidx] = tg;
	      }
	  }
      }

  updateUserMask();
}

void
ImageWidget::removeDotImage(int x, int y)
{
  if (! withinBounds(x, y))
    return ;
		    
  int xstart = qMax(0, x-Global::spread());
  int xend = qMin(m_imgWidth, x+Global::spread());
  int ystart = qMax(0, y-Global::spread());
  int yend = qMin(m_imgHeight, y+Global::spread());

  const uchar *bits = m_mainusermask.bits();
  for(int i=xstart; i<xend; i++)
    for(int j=ystart; j<yend; j++)
      {
	int xi = qAbs(i-x);
	int yj = qAbs(j-y);
	float frc = ((float)(xi*xi + yj*yj)/
		     (float)(Global::spread()*Global::spread()));
	if (frc < 1)
	  {
	    int sidx = j*m_imgWidth + i;
	    if (m_sliceImage[4*sidx+3] > 0)
	      {
		m_mainusermask.setPixel(i, j, 0);
		m_prevtags[sidx] = 0;
	      }
	  }
      }

  updateUserMask();
}

void
ImageWidget::addImage()
{
  const uchar *bits = m_maskpad.bits();
  for(int x=0; x<m_imgWidth; x++)
    for(int y=0; y<m_imgHeight; y++)
      {
	int sidx = y*m_imgWidth+x;
	if (bits[4*sidx+3] > 0)
	  {
	    if (m_areaTool)
	      m_prevtags[sidx] = Global::tag();
	    else
	      m_prevtags[sidx] = m_tags[sidx];

	    int tg = m_prevtags[sidx];
	    uchar r = m_tagColors[4*tg+0];
	    uchar g = m_tagColors[4*tg+1];
	    uchar b = m_tagColors[4*tg+2];
	    uchar a = 200;
	    m_mainusermask.setPixel(x, y, qRgba(r,g,b,a));

	    //m_mainusermask.setPixel(x, y, qRgba(150, 50, 150, 150));
	  }
      }

  updateUserMask();
}

void
ImageWidget::removeImage()
{
  const uchar *bits = m_maskpad.bits();
  for(int x=0; x<m_imgWidth; x++)
    for(int y=0; y<m_imgHeight; y++)
      {
	int sidx = y*m_imgWidth+x;
	if (bits[4*sidx] > 0)
	  {
	    m_mainusermask.setPixel(x, y, 0);
	    m_prevtags[sidx] = 0;
	  }
      }

  updateUserMask();
}

void
ImageWidget::emitFill(bool slice)
{
  if (slice)
    {
      //if (m_propagateTagging)
	{
	  if (m_sliceType == DSlice)
	    emit tagDSlice(m_currSlice, m_tags);
	  else if (m_sliceType == WSlice)
	    emit tagWSlice(m_currSlice, m_tags);
	  else if (m_sliceType == HSlice)
	    emit tagHSlice(m_currSlice, m_tags);
	}
//      else
//	{
//	  if (m_sliceType == DSlice)
//	    emit tagDSlice(m_currSlice, m_usermask);
//	  else if (m_sliceType == WSlice)
//	    emit tagWSlice(m_currSlice, m_usermask);
//	  else if (m_sliceType == HSlice)
//	    emit tagHSlice(m_currSlice, m_usermask);
//	}

      return;
    }


  QList<int> dwh;
  dwh.clear();
  
  //const uchar *bits = m_usermask.bits();

  if (m_sliceType == DSlice)
    {
      for(int i=0; i<m_imgWidth; i++)
	for(int j=0; j<m_imgHeight; j++)
	  {
	    int idx = j*m_imgWidth + i;
	    //if (bits[4*idx] > 0)
	    if (m_tags[idx] > 0)
	      {
		dwh << m_currSlice; // depth
		dwh << j;           // width
		dwh << i;           // height
	      }
	  }
    }
  else if (m_sliceType == WSlice)
    {
      for(int i=0; i<m_imgWidth; i++)
	for(int j=0; j<m_imgHeight; j++)
	  {
	    int idx = j*m_imgWidth + i;
	    if (m_tags[idx] > 0)
	      {
		dwh << j;            // depth
		dwh << m_currSlice;  // width
		dwh << i;            // height
	      }
	  }
    }
  else
    {
      for(int i=0; i<m_imgWidth; i++)
	for(int j=0; j<m_imgHeight; j++)
	  {
	    int idx = j*m_imgWidth + i;
	    if (m_tags[idx] > 0)
	      {
		dwh << j;            // depth
		dwh << i;            // width
		dwh << m_currSlice;  // height
	      }
	  }
    }

  QMessageBox::information(0, "", QString("%1").arg(dwh.count()));

  emit fillVolume(m_minDSlice, m_maxDSlice,
		  m_minWSlice, m_maxWSlice,
		  m_minHSlice, m_maxHSlice,
		  dwh,
		  slice);
}

void
ImageWidget::updateUserMask()
{
  m_maskimage = QImage(m_prevtags,
		       m_imgWidth,
		       m_imgHeight,
		       m_imgWidth,
		       QImage::Format_Indexed8);
  m_maskimage.setColorTable(m_tagColors);
  m_maskimageScaled = m_maskimage.scaled(m_simgWidth,
					 m_simgHeight,
					 Qt::IgnoreAspectRatio,
					 Qt::SmoothTransformation);

  //-------------------------
  m_difftagimage.fill(0);
  for(int j=0; j<m_imgHeight; j+=2)
    for(int i=0; i<m_imgWidth; i+=2)
      {
	int idx = j*m_imgWidth + i;
	if (m_prevtags[idx] != m_maskslice[idx])
	  m_difftagimage.setPixel(i, j, qRgba(0, 0, 0, 200));
      }

  m_difftagimageScaled = m_difftagimage.scaled(m_simgWidth,
					       m_simgHeight,
					       Qt::IgnoreAspectRatio,
					       Qt::SmoothTransformation);
  //-------------------------
//  maskMask(&m_usermask);
//
//  m_usermaskScaled = m_usermask.scaled(m_simgWidth,
//				       m_simgHeight,
//				       Qt::IgnoreAspectRatio,
//				       Qt::SmoothTransformation);  
}

void
ImageWidget::tagUntaggedRegion()
{
  int ist, ied, jst, jed;
  if (m_sliceType == DSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minWSlice;
      jed = m_maxWSlice;
    }
  else if (m_sliceType == WSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }
  else
    {
      ist = m_minWSlice;
      ied = m_maxWSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }

  uchar currtag = 0;
  for(int i=0; i<m_imgWidth*m_imgHeight; i++)
    currtag = qMax(currtag, m_tags[i]);


  QStack<int> stack;

  bool done = false;
  while (!done)
    {
      currtag ++;
      if (currtag == 255)
	{
	  QMessageBox::information(0, "", QString("num of tags > 255"));
	  currtag = 1;
	}

      int x = -1;
      int y = -1;
      for(int i=ist; i<ied; i++)
	for(int j=jst; j<jed; j++)
	  {
	    int sidx = j*m_imgWidth + i;
	    if (m_sliceImage[4*sidx+3] > 0 &&
		m_tags[sidx] == 0)
	      {
		m_tags[sidx] = currtag;
		x = i; y = j;
		i = m_imgWidth;
		j = m_imgHeight;
	      }
	  }

      if (x == -1 || y == -1)
	{
	  done = true;
	  break;
	}

      stack.clear();
      stack.push(x); stack.push(y);
      int ns = 1;
      while(!stack.isEmpty())
	{
	  y = stack.pop();
	  x = stack.pop();

	  int imin = qMax(ist, x-1);
	  int jmin = qMax(jst, y-1);
	  int imax = qMin(ied, x+1);
	  int jmax = qMin(jed, y+1);

	  for(int j=jmin; j<=jmax; j++)
	    for(int i=imin; i<=imax; i++)
	      {
		int sidx = j*m_imgWidth + i;
		if (m_sliceImage[4*sidx+3] > 0 &&
		    m_tags[sidx] == 0)
		  {
		    m_tags[sidx] = currtag;
		    stack.push(i); stack.push(j);
		    ns++;
		  }
	      }
	}
    }
}

void
ImageWidget::propagateTaggedRegion()
{
  memset(m_tags, 0, m_imgWidth*m_imgHeight);

  int ist, ied, jst, jed;
  if (m_sliceType == DSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minWSlice;
      jed = m_maxWSlice;
    }
  else if (m_sliceType == WSlice)
    {
      ist = m_minHSlice;
      ied = m_maxHSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }
  else
    {
      ist = m_minWSlice;
      ied = m_maxWSlice;

      jst = m_minDSlice;
      jed = m_maxDSlice;
    }


  for(int i=ist; i<ied; i++)
    for(int j=jst; j<jed; j++)
      {
	int sidx = j*m_imgWidth + i;
	if (m_sliceImage[4*sidx+3] > 0 &&
	    m_prevtags[sidx] > 0)
	  {
	    m_tags[sidx] = m_prevtags[sidx];
	  }
      }

  uchar maxtag = 0;
  for(int i=0; i<m_imgWidth*m_imgHeight; i++)
    maxtag = qMax(maxtag, m_tags[i]);

  for(uchar currtag=1; currtag<=maxtag; currtag++)
    {
      QStack<int> stack;
      stack.clear();

      for(int i=ist; i<ied; i++)
	for(int j=jst; j<jed; j++)
	  {
	    int sidx = j*m_imgWidth + i;
	    if (m_sliceImage[4*sidx+3] > 0 &&
		m_tags[sidx] == currtag)
	      {
		stack.push(i); stack.push(j);
	      }
	  }


      while(!stack.isEmpty())
	{
	  int y = stack.pop();
	  int x = stack.pop();

	  int imin = qMax(ist, x-1);
	  int jmin = qMax(jst, y-1);
	  int imax = qMin(ied, x+1);
	  int jmax = qMin(jed, y+1);
	  
	  for(int j=jmin; j<=jmax; j++)
	    for(int i=imin; i<=imax; i++)
	      {
		int sidx = j*m_imgWidth + i;
		if (m_sliceImage[4*sidx+3] > 0 &&
		    m_tags[sidx] == 0)
		  {
		    m_tags[sidx] = currtag;
		    stack.push(i); stack.push(j);
		  }
	      }
	}
    }
}

void
ImageWidget::copyTaggedRegion()
{
  m_mainusermask.fill(0);
  for(int x=0; x<m_imgWidth; x++)
    for(int y=0; y<m_imgHeight; y++)
      {
	int sidx = y*m_imgWidth+x;
	if (m_tags[sidx] > 0)
	  {
	    int tg = m_tags[sidx];
	    uchar r = m_tagColors[4*tg+0];
	    uchar g = m_tagColors[4*tg+1];
	    uchar b = m_tagColors[4*tg+2];
	    uchar a = 200;
	    m_mainusermask.setPixel(x, y, qRgba(r,g,b,a));
	  }
      }

  updateUserMask();
  update();
}

void
ImageWidget::tagSlice()
{
  propagateTaggedRegion();

  if (m_automaticTagging)
    tagUntaggedRegion();

  copyTaggedRegion();

  generateMaskImage();

  emitFill(true);
}

void
ImageWidget::generateMaskImage()
{
  m_maskimage = QImage(m_tags,
		       m_imgWidth,
		       m_imgHeight,
		       m_imgWidth,
		       QImage::Format_Indexed8);
  m_maskimage.setColorTable(m_tagColors);
  m_maskimageScaled = m_maskimage.scaled(m_simgWidth,
					 m_simgHeight,
					 Qt::IgnoreAspectRatio,
					 Qt::SmoothTransformation);  

  calcDiffTags();
}
